package com.zmx.b2c.order.service;

import com.nepxion.aquarius.cache.annotation.CacheEvict;
import com.zmx.b2c.order.common.CompletableFutureThreadPool;
import com.zmx.common.common.contents.cache.OrderCacheContents;
import com.zmx.b2c.order.config.SecretConfig;
import com.zmx.b2c.order.contents.CreateOrderContents;
import com.zmx.b2c.order.dto.*;
import com.zmx.b2c.order.dto.params.*;
import com.zmx.b2c.order.dto.result.*;
import com.zmx.b2c.order.entity.B2COrder;
import com.zmx.b2c.order.repository.B2COrderRepository;
import com.zmx.b2c.order.service.mapper.B2COrderMapper;
import com.zmx.b2c.pay.dto.PaymentDto;
import com.zmx.b2c.pay.entity.Payment;
import com.zmx.b2c.pay.repository.PaymentRepository;
import com.zmx.b2c.pay.service.PaymentService;
import com.zmx.common.common.contents.MqQueueContents;
import com.zmx.common.common.enums.Enum;
import com.zmx.common.common.exception.BusinessException;
import com.zmx.common.common.invocation.InvocationInfoProxy;
import com.zmx.common.common.properties.ApplicationProperties;
import com.zmx.common.common.service.BaseService;
import com.zmx.common.common.utils.AESUtil;
import com.zmx.common.common.utils.Lock.ZookeeperLock;
import com.zmx.common.common.utils.SignUtils;
import com.zmx.common.common.utils.id.Id;
import com.zmx.product.api.ProductApi;
import com.zmx.product.dto.ProductDto;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Description: 订单实现类
 * @ClassName: B2COrderService
 * @Author zhaomxr
 * @Date 2021-10-22 15:00
 */
@Slf4j
@Service
public class B2COrderService extends BaseService<B2COrder, B2COrderDto, B2COrderRepository, B2COrderMapper> {

    @Autowired
    private SecretConfig secretConfig;
    @Autowired
    private AmqpTemplate amqpTemplate;
    @Autowired
    private ApplicationProperties applicationProperties;
    @Autowired
    private Id id;
    @Autowired
    private B2COrderRepository b2COrderRepository;
    @Autowired
    private ZookeeperLock zookeeperLock;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private ProductApi productApi;
    @Autowired
    private ToCreateOrderService toCreateOrderService;
    @Autowired
    private PaymentService paymentService;
    @Autowired
    private PaymentRepository paymentRepository;
    @Autowired
    private B2COrdercancelService b2COrdercancelService;
    @Autowired
    private B2COrderItemService b2COrderItemService;


    /**
     * 构建订单显示信息
     *
     * @param params
     * @return
     */
    public OrderShowDTO orderShow(OrderShowParamsDTO params) {
        List<ProductOrderShowDTO> productOrderShowDTOs = new ArrayList<ProductOrderShowDTO>();

        //多线程构建显示订单，加快处理速度
        CompletableFutureThreadPool completableFutureThreadPool = new CompletableFutureThreadPool();
        ThreadPoolExecutor pool = completableFutureThreadPool.poolExecutor();
        List<CompletableFuture<OrderShowDTO>> futureList = new ArrayList<CompletableFuture<OrderShowDTO>>(2);

        //构建商品信息
        CompletableFuture<OrderShowDTO> productFuture = CompletableFuture.supplyAsync(() -> {
            List<Long> productIds = params.getProductParamsDTOs().stream().map(ProductParamsDTO::getProductId).collect(Collectors.toList());
            List<ProductDto> productDtos = productApi.findByIdIn(productIds.toArray(new Long[0]));

            OrderShowDTO orderShowDTO = new OrderShowDTO();
            BigDecimal amountPaid = BigDecimal.ZERO;
            BigDecimal freight = BigDecimal.ZERO;
            BigDecimal totalPrice = BigDecimal.ZERO;
            for (ProductParamsDTO productParamsDTO : params.getProductParamsDTOs()) {
                List<ProductDto> productDtoList = productDtos.stream().filter(productDto -> {
                    return Objects.equals(productDto.getId(), productParamsDTO.getProductId());
                }).collect(Collectors.toList());
                ProductDto productDto = productDtoList.get(0);

                ProductOrderShowDTO productOrderShowDTO = new ProductOrderShowDTO();
                BeanUtils.copyProperties(productDto, productOrderShowDTO);

                //计算支付金额，运费，订单总金额
                amountPaid = amountPaid.add(productOrderShowDTO.getPrice().multiply(BigDecimal.valueOf(productParamsDTO.getQuantity())));
                freight = freight.add(productOrderShowDTO.getFreight().multiply(BigDecimal.valueOf(productParamsDTO.getQuantity())));
                totalPrice = totalPrice.add(productOrderShowDTO.getPrice().multiply(BigDecimal.valueOf(productParamsDTO.getQuantity())));
                productOrderShowDTO.setQuantity(productParamsDTO.getQuantity());
                productOrderShowDTOs.add(productOrderShowDTO);
            }

            DecimalFormat df = new DecimalFormat("#.00");
            String amountPaidS = df.format(amountPaid);
            String freightS = df.format(freight);
            String totalPriceS = df.format(totalPrice);

            orderShowDTO.setProductDTOs(productOrderShowDTOs);
            orderShowDTO.setFreight(BigDecimal.valueOf(Double.valueOf(freightS)));
            orderShowDTO.setAmountPaid(BigDecimal.valueOf(Double.valueOf(amountPaidS)));
            orderShowDTO.setTotalPrice(BigDecimal.valueOf(Double.valueOf(totalPriceS)).add(BigDecimal.valueOf(Double.valueOf(freightS))));
            return orderShowDTO;
        }, pool);
        futureList.add(productFuture);

        //构建签名
        CompletableFuture<OrderShowDTO> signFuture = CompletableFuture.supplyAsync(() -> {
            OrderShowDTO orderShowDTO = new OrderShowDTO();
            String sign = "";
            String timeStamp = String.valueOf(System.currentTimeMillis());
            try {
                //将时间戳放入redis，验签时，如果时间戳过期 则验签失败
                String memberId = InvocationInfoProxy.getUserId();
                redisTemplate.opsForValue().set(AESUtil.aesEncrypt(memberId, secretConfig.getAesKey()), timeStamp, 1800, TimeUnit.SECONDS);

                //构建签名
                Map<String, String> signMap = new HashMap<String, String>();
                signMap.put("openId", params.getOpenId());
                signMap.put("memberId", AESUtil.aesEncrypt(memberId, secretConfig.getAesKey()));
                signMap.put("timeStamp", timeStamp);
                sign = SignUtils.getSign(signMap, secretConfig.getSignSecretKey());
                log.info("构建签名验证参数 signMap=" + signMap.toString());
                log.info("签名 sign=" + sign);
            } catch (Exception e) {
                log.info("构建签名失败！");
                throw new BusinessException("构建签名失败！");
            }

            orderShowDTO.setSign(sign);
            orderShowDTO.setTimeStamp(String.valueOf(timeStamp));
            return orderShowDTO;
        }, pool);
        futureList.add(signFuture);
        //所有任务都执行完成并返回
        CompletableFuture.allOf(futureList.toArray(new CompletableFuture[futureList.size()])).join();

        OrderShowDTO sign = signFuture.join();
        OrderShowDTO product = productFuture.join();
        pool.shutdown();

        OrderShowDTO orderShowDTO = new OrderShowDTO();
        orderShowDTO.setProductDTOs(product.getProductDTOs());
        orderShowDTO.setFreight(product.getFreight());
        orderShowDTO.setAmountPaid(product.getAmountPaid());
        orderShowDTO.setTotalPrice(product.getTotalPrice());
        orderShowDTO.setSign(sign.getSign());
        orderShowDTO.setTimeStamp(sign.getTimeStamp());
        return orderShowDTO;
    }

    @Transactional(rollbackFor = Exception.class)
    public OrderParamsDTO createOrder(CreateOrderParams params) {
        //创建订单参数
        OrderParamsDTO orderParams = toCreateOrderService.createOrderParams(params);
        //创建订单，订单详情，订单日志
        OrderParamsDTO orderParamsDTO = toCreateOrderService.toCreateOrder(orderParams);
        //创建支付记录，支付表，支付日志
        toCreateOrderService.toCreatePayment(orderParamsDTO);

        //释放订单预下单
        this.release(params.getOnlyNumber());
        //订单入死信队列,异步执行
        toCreateOrderService.sendTimeOutOrder(orderParamsDTO.getOrderSn());
        return orderParamsDTO;
    }

    /**
     * 计算订单总金额
     * @param paramsDTOList
     * @return
     */
    public SumOrderResultDTO sumOrderTotalAmount(List<ProductParamsDTO> paramsDTOList) {
        return sumOrderAmount(paramsDTOList);
    }

    /**
     * 取消订单
     * @param orderSn
     * @return
     */
    public void cancelOrder(String orderSn) {
        B2COrderDto b2COrderDto = this.getOrderByCode(orderSn);

        if (!Objects.equals(InvocationInfoProxy.getUserId(), b2COrderDto.getMemberId())) {
            throw new BusinessException(CreateOrderContents.NO_USER_ORDER_ERROR_MSG);
        }
        if (b2COrderDto == null) {
            throw new BusinessException(CreateOrderContents.ORDER_NO_EXIT_MSG);
        } else if (b2COrderDto.getDeliveryStatus() == 2) {
            throw new BusinessException(CreateOrderContents.ORDER_DELIVERY_MSG);
        } else if (b2COrderDto.getOrderStatus() != 0 && b2COrderDto.getOrderStatus() != 1) {
            throw new BusinessException(CreateOrderContents.ORDER_NO_CANCEL_MSG);
        }

        Boolean isPayment = false;
        List<Payment> paymentList = paymentRepository.findByOrders(b2COrderDto.getId());
        //遍历支付记录，全部支付或者部分支付都视为已支付
        for (Payment payment : paymentList) {
            if (payment.getStatus() == Enum.PaymentStatus.paid.getCode()) {
                isPayment = true;
            }
        }

        B2COrderDto order = new B2COrderDto();
        order.setId(b2COrderDto.getId());
        if (b2COrderDto.getPaymentStatus() == Enum.PaymentStatus.paid.getCode() && isPayment) {
            order.setOrderStatus(Enum.OrderStatus.incancel.getCode());
            //插入取消订单队列表
            B2COrdercancelDto b2COrdercancelDto = new B2COrdercancelDto();
            b2COrdercancelDto.setOrders(b2COrderDto.getId());
            b2COrdercancelDto.setOrderSn(b2COrderDto.getOrderSn());
            b2COrdercancelDto.setStatus(0);
            b2COrdercancelDto.setSource(1);
            b2COrdercancelDto.setReason("客户取消订单");
            b2COrdercancelDto.setCreator(InvocationInfoProxy.getUserId());
            b2COrdercancelService.create(b2COrdercancelDto);
            log.info("插入取消订单队列表 订单编号: {}" + b2COrderDto.getOrderSn());
        } else if (b2COrderDto.getPaymentStatus() == Enum.OrderStatus.unconfirmed.getCode() && !isPayment) {
            //直接取消订单
            order.setOrderStatus(Enum.OrderStatus.canceled.getCode());
            log.info("订单已取消 订单编号=> " + b2COrderDto.getOrderSn());
        }
        this.update(order);

        //记录取消订单日志,异步执行
        B2COrderLogDto b2COrderLogDto = new B2COrderLogDto();
        b2COrderLogDto.setOrders(b2COrderDto.getId());
        b2COrderLogDto.setType(Enum.orderLogType.customerCancel.ordinal());
        b2COrderLogDto.setOperator("create by member=> " + InvocationInfoProxy.getUserId());
        b2COrderLogDto.setContent("用户自主取消订单");
        toCreateOrderService.createB2COrderLogDto(b2COrderLogDto);
    }

    /**
     * 确认收货
     * @param orderSn
     * @return
     */
    public void shippedOrder(String orderSn){
        this.updateOrderStatus(orderSn,Enum.OrderStatus.completed);
    }

    /**
     * 修改订单状态
     * @param orderSn
     * @param orderStatus
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateOrderStatus(String orderSn, Enum.OrderStatus orderStatus) {
        B2COrderDto b2COrderDto = this.getOrderByCode(orderSn);

        if (!Objects.equals(InvocationInfoProxy.getUserId(), b2COrderDto.getMemberId())) {
            throw new BusinessException(CreateOrderContents.ORDER_MEMBERID_ERROR_MSG);
        }

        B2COrderDto order = new B2COrderDto();
        order.setId(b2COrderDto.getId());
        order.setOrderStatus(orderStatus.getCode());
        this.update(order);
    }

    /**
     * 支付成功后回写订单
     */
    @Transactional(rollbackFor = Exception.class)
    public void payAfterRewriteOrder(B2COrderDto b2COrderDto, BigDecimal totalPrice) {
        //校验订单金额与回写金额是否一致
        if (totalPrice.compareTo(b2COrderDto.getTotalPrice()) != 0) {
            throw new BusinessException(CreateOrderContents.PAY_AFTER_REWRITE_ORDER_MSG);
        }

        B2COrderDto order = new B2COrderDto();
        order.setId(b2COrderDto.getId());
        order.setPaymentStatus(Enum.PaymentStatus.paid.getCode());
        this.update(order);
    }

    /** ------------------------------显示订单信息---------------------- **/

    /**
     * 分页查询订单
     * @param searchOrderDTO
     * @return
     */
    public Page getOrderPage(SearchOrderParamsDTO searchOrderDTO) {
        Map params = new HashMap<String, Object>(5);
        params.put("memberId", InvocationInfoProxy.getUserId());
        params.put("platform", searchOrderDTO.getPlatform());
        params.put("orderStatus", searchOrderDTO.getOrderStatus());
        params.put("paymentStatus", searchOrderDTO.getPaymentStatus());
        params.put("orderSn", searchOrderDTO.getOrderSn());
        //分页查询订单
        Page<B2COrderDto> page = this.getAll(params, searchOrderDTO.getPageIndex(),searchOrderDTO.getPageSize());

        List<OrderPageResultDTO> resultPageDTOs = new ArrayList<OrderPageResultDTO>();
        if (page != null && page.hasContent()) {
            for (B2COrderDto b2COrderDto : page.getContent()) {
                OrderPageResultDTO orderPageResultDTO = new OrderPageResultDTO();
                List<B2COrderItemDto> orderItemList = b2COrderItemService.findByOrderId(b2COrderDto.getId());

                OrderResultDTO orderResultDTO = new OrderResultDTO();
                BeanUtils.copyProperties(b2COrderDto, orderResultDTO);
                orderResultDTO.setOrderStatusStr(getOrderStatusStr(b2COrderDto.getOrderStatus(), b2COrderDto.getPaymentStatus(), b2COrderDto.getDeliveryStatus()));

                List<Long> productIds = orderItemList.stream().map(B2COrderItemDto::getProductId).collect(Collectors.toList());
                List<ProductDto> productDtos = productApi.findByIdIn(productIds.toArray(new Long[0]));

                int total_quantity = 0;
                List<OrderItemResultDTO> orderItemResultDTOS = new ArrayList<OrderItemResultDTO>(orderItemList.size());
                for (B2COrderItemDto orderItemDTO : orderItemList) {
                    total_quantity = total_quantity + orderItemDTO.getQuantity();

                    List<ProductDto> productDtoList = productDtos.stream().filter(productDto -> {
                        return Objects.equals(productDto.getId(), orderItemDTO.getProductId());
                    }).collect(Collectors.toList());
                    ProductDto productDto = productDtoList.get(0);

                    OrderItemResultDTO orderItemResultDTO = new OrderItemResultDTO();
                    BeanUtils.copyProperties(orderItemDTO, orderItemResultDTO);
                    orderItemResultDTO.setImage(productDto.getImage());
                    orderItemResultDTO.setSpecification(productDto.getSpecification());
                    orderItemResultDTO.setSize(productDto.getSize());
                    orderItemResultDTOS.add(orderItemResultDTO);
                }
                orderResultDTO.setTotalQuantity(total_quantity);

                //构建订单返回数据
                orderPageResultDTO.setOrderResultDTO(orderResultDTO);
                orderPageResultDTO.setOrderItemResultDTOS(orderItemResultDTOS);
                resultPageDTOs.add(orderPageResultDTO);
            }
        }

        return new PageImpl(resultPageDTOs,page.getPageable(),page.getTotalElements());
    }

    /**
     * 取订单详细信息
     * @param orderId
     * @return
     */
    public OrderDetailResultDTO getOrderDetailByOrderId(Long orderId) {
        B2COrder b2COrder = this.findById(orderId);
        List<B2COrderItemDto> orderItemList = b2COrderItemService.findByOrderId(b2COrder.getId());

        B2COrderDto b2COrderDto = entityToDto(b2COrder);
        b2COrderDto.setOrderStatusStr(getOrderStatusStr(b2COrder.getOrderStatus(), b2COrder.getPaymentStatus(), b2COrder.getDeliveryStatus()));

        //订单商品信息
        List<Long> productIds = orderItemList.stream().map(B2COrderItemDto::getProductId).collect(Collectors.toList());
        List<ProductDto> productDtos = productApi.findByIdIn(productIds.toArray(new Long[0]));

        List<ProductOrderShowDTO> productDTOs = new ArrayList<ProductOrderShowDTO>();
        for (B2COrderItemDto orderItemDTO : orderItemList) {
            List<ProductDto> productDtoList = productDtos.stream().filter(productDto -> {
                return Objects.equals(productDto.getId(), orderItemDTO.getProductId());
            }).collect(Collectors.toList());
            ProductDto productDto = productDtoList.get(0);

            ProductOrderShowDTO productOrderShowDTO = new ProductOrderShowDTO();
            BeanUtils.copyProperties(productDto, productOrderShowDTO);
            productOrderShowDTO.setQuantity(orderItemDTO.getQuantity());
            productDTOs.add(productOrderShowDTO);
        }

        //支付金额
        BigDecimal paymentAmount = BigDecimal.ZERO;
        Date paymentDate = null;
        List<PaymentDto> paymentDtos = paymentService.findByOrders(b2COrder.getId());
        for (PaymentDto paymentDto : paymentDtos) {
            if (paymentDto.getStatus() == Enum.PaymentStatus.paid.getCode()) {
                paymentAmount = paymentAmount.add(paymentDto.getAmount());
                paymentDate = paymentDto.getModifyTime();
            }
        }

        OrderDetailResultDTO orderDetailResultDTO = new OrderDetailResultDTO();
        orderDetailResultDTO.setB2COrderDto(b2COrderDto);
        orderDetailResultDTO.setOrderItemDTOs(orderItemList);
        orderDetailResultDTO.setProductDTOs(productDTOs);
        orderDetailResultDTO.setPaymentAmount(paymentAmount);
        orderDetailResultDTO.setPaymentDate(paymentDate);
        return orderDetailResultDTO;
    }

    /**
     * 根据订单编号取订单详细信息
     * @param orderSn
     * @return
     */
    public OrderDetailResultDTO getOrderDetailByOrderSn(String orderSn) {
        B2COrder b2COrder = repository.findByOrderSn(orderSn);
        List<B2COrderItemDto> orderItemList = b2COrderItemService.findByOrderId(b2COrder.getId());

        toCreateOrderService.sendTimeOutOrder(b2COrder.getOrderSn());

        B2COrderDto b2COrderDto = entityToDto(b2COrder);
        b2COrderDto.setOrderStatusStr(getOrderStatusStr(b2COrder.getOrderStatus(), b2COrder.getPaymentStatus(), b2COrder.getDeliveryStatus()));

        //订单商品信息
        List<Long> productIds = orderItemList.stream().map(B2COrderItemDto::getProductId).collect(Collectors.toList());
        List<ProductDto> productDtos = productApi.findByIdIn(productIds.toArray(new Long[0]));

        List<ProductOrderShowDTO> productDTOs = new ArrayList<ProductOrderShowDTO>();
        for (B2COrderItemDto orderItemDTO : orderItemList) {
            List<ProductDto> productDtoList = productDtos.stream().filter(productDto -> {
                return Objects.equals(productDto.getId(), orderItemDTO.getProductId());
            }).collect(Collectors.toList());
            ProductDto productDto = productDtoList.get(0);

            ProductOrderShowDTO productOrderShowDTO = new ProductOrderShowDTO();
            BeanUtils.copyProperties(productDto, productOrderShowDTO);
            productOrderShowDTO.setQuantity(orderItemDTO.getQuantity());
            productDTOs.add(productOrderShowDTO);
        }

        //支付金额
        BigDecimal paymentAmount = BigDecimal.ZERO;
        Date paymentDate = null;
        List<PaymentDto> paymentDtos = paymentService.findByOrders(b2COrder.getId());
        for (PaymentDto paymentDto : paymentDtos) {
            if (paymentDto.getStatus() == Enum.PaymentStatus.paid.getCode()) {
                paymentAmount = paymentAmount.add(paymentDto.getAmount());
                paymentDate = paymentDto.getModifyTime();
            }
        }

        Boolean isPayment = true;
        String orderShowMessage = "";
        int orderStatus = b2COrderDto.getOrderStatus();
        int paymentStatus = b2COrderDto.getPaymentStatus();
        int deliveryStatus = b2COrderDto.getDeliveryStatus();
        if ((orderStatus == Enum.OrderStatus.unconfirmed.getCode() || orderStatus == Enum.OrderStatus.confirmed.getCode()) && paymentStatus == Enum.PaymentStatus.unpaid.getCode()) {
            isPayment = false;
        } else if ((orderStatus == Enum.OrderStatus.unconfirmed.getCode() || orderStatus == Enum.OrderStatus.confirmed.getCode()) && paymentStatus == Enum.PaymentStatus.paid.getCode() && deliveryStatus == Enum.DeliveryStatus.undelivery.getCode()) {
            orderShowMessage = "您的订单正在配送途中,请耐心等待!";
        } else if (orderStatus == Enum.OrderStatus.canceled.getCode()) {
            orderShowMessage = "您的订单已取消!";
        } else if (orderStatus == Enum.OrderStatus.incancel.getCode()) {
            orderShowMessage = "您的订单正在取消中,请耐心等待!";
        } else if (orderStatus == Enum.OrderStatus.completed.getCode()) {
            orderShowMessage = "您的订单已完成,感谢您的选择!";
        }

        OrderDetailResultDTO orderDetailResultDTO = new OrderDetailResultDTO();
        orderDetailResultDTO.setB2COrderDto(b2COrderDto);
        orderDetailResultDTO.setOrderItemDTOs(orderItemList);
        orderDetailResultDTO.setProductDTOs(productDTOs);
        orderDetailResultDTO.setPaymentAmount(paymentAmount);
        orderDetailResultDTO.setPaymentDate(paymentDate);
        orderDetailResultDTO.setIsPayment(isPayment);
        orderDetailResultDTO.setOrderShowMessage(orderShowMessage);
        return orderDetailResultDTO;
    }

    /**
     * 判断订单状态
     */
    public String getOrderStatusStr(Integer orderStatus, Integer paymentStatus, Integer deliveryStatus) {
        if (orderStatus == Enum.OrderStatus.completed.getCode()) {
            return "已完成";
        } else if (orderStatus == Enum.OrderStatus.incancel.getCode()) {
            return "取消中";
        } else if (orderStatus == Enum.OrderStatus.canceled.getCode()) {
            return "已取消";
        } else if (orderStatus == Enum.OrderStatus.reject.getCode()) {
            return "拒收";
        } else if (orderStatus == Enum.OrderStatus.rejected.getCode()) {
            return "拒收入库";
        } else if (paymentStatus == Enum.PaymentStatus.unpaid.getCode()) {
            return "未支付";
        } else if (paymentStatus == Enum.PaymentStatus.paid.getCode()) {
            if (deliveryStatus == Enum.DeliveryStatus.undelivery.getCode()) {
                return "待发货";
            } else if (deliveryStatus == Enum.DeliveryStatus.delivery.getCode()) {
                return "已发货";
            }
        }
        return "已确认";
    }
    /** ------------------------------显示订单信息---------------------- **/


    /**
     * 修改订单状态
     * 用于测试redis与数据库数据不一致情况
     * 采用先更新数据库，再删除缓存方式（推荐） | 如果是热点key比较多情况下，用先更新数据库，再更新缓存方式
     * 1.分布式锁对当前更新数据加锁，保证并发数据一致性
     * 2.修改订单状态
     * 3.此订单加入延时队列中，补偿删除对应缓存中数据
     * 3.删除缓存
     *
     * @param orderCode
     * @return
     */
    @CacheEvict(key = "#orderCode", beforeInvocation = false)
    @Transactional(rollbackFor = Exception.class)
    public B2COrderDto updateOrderStatusByCode(String orderCode, Enum.OrderStatus orderStatus) {
        Assert.notNull(orderCode, "订单编号为空");

        B2COrder save = null;
        InterProcessMutex lock = null;
        try {
            lock = zookeeperLock.tryLock("updateOrderStatus" + orderCode, 3000, 3000);
            if (lock == null) {
                throw new BusinessException("当前订单正在修改中，请稍后操作！");
            }

            //修改订单状态
            B2COrder orderEntity = b2COrderRepository.findByOrderSn(orderCode);
            orderEntity.setOrderStatus(orderStatus.getCode());
            orderEntity.setModifyTime(new Date());
            save = b2COrderRepository.save(orderEntity);
            log.info("修改订单 {} 状态 {} -> {}", orderCode, Enum.OrderStatus.getByCode(orderEntity.getOrderStatus()).getMessage(), orderStatus.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (lock != null) {
                try {
                    zookeeperLock.unlock(lock);
                } catch (Exception e) {
                    log.error("解锁失败", e);
                }
            }
        }
        return entityToDto(save);
    }

    /**
     * 补偿 清除订单缓存
     *
     * @param orderCode
     */
    private void deleteOrderCache(String orderCode) {
        try {
            //删除缓存
            Boolean delete = redisTemplate.delete(applicationProperties.getApplication().getName() + "_" + OrderCacheContents.ORDER + "_" + orderCode);
            //如果删除缓存失败，加入延时队列，利用重试机制进行补偿删除
            if (!delete) {
                amqpTemplate.convertAndSend(MqQueueContents.DELETE_ORDERCACHE_EXCHANGE, "", orderCode);
            }
        } catch (AmqpException e) {
            log.error(e.getMessage(), e);
        }
    }

    public B2COrderDto getOrderByCode(String orderCode) {
        if (StringUtils.isEmpty(orderCode)) {
            throw new BusinessException("订单号为null");
        }

        B2COrder orderEntity = b2COrderRepository.findByOrderSn(orderCode);
        B2COrderDto b2COrderDto = entityToDto(orderEntity);
        List<B2COrderItemDto> b2COrderItemDtos = b2COrderItemService.findByOrderId(orderEntity.getId());
        b2COrderDto.setB2COrderItemDtos(b2COrderItemDtos);

        return b2COrderDto;
    }

    @Override
    public B2COrder dtoToEntity(B2COrderDto dto) {
        //code后期改造成基于编码规则生成
        dto.setOrderSn(id.generateCode("DD"));
        return super.dtoToEntity(dto);
    }

    /**
     * 校验订单参数
     * @param createOrderParams
     */
    public void checkOrderParams(CreateOrderParams createOrderParams) {
        String onlyNumber = createOrderParams.getOnlyNumber();
        //检查订单是否重复提交
        this.check(onlyNumber);

        //校验验证码
        if (!StringUtils.isEmpty(createOrderParams.getOpenId())) {
            Boolean checkFlag = checkSign(createOrderParams.getOpenId(), createOrderParams.getMemberId(), createOrderParams.getTimeStamp(), createOrderParams.getSign());
            if (!checkFlag) {
                throw new BusinessException(CreateOrderContents.SIGN_ERROR_MSG);
            }
        }

        //判断商品是否符合要求
        for (ProductParamsDTO productParamsDTO : createOrderParams.getProductParamsDTOs()) {
            if (productParamsDTO.getQuantity() <= 0) {
                throw new BusinessException(CreateOrderContents.PRODUCT_ERROR_MSG);
            }
            ProductDto productDto = productApi.findById(productParamsDTO.getProductId());
            //判断商品是否存在
            if (productDto == null) {
                throw new BusinessException(CreateOrderContents.PRO_NO_EXIT_MSG);
            } else if (productDto.getMarketable() == 0) {
                //判断商品是否下架
                throw new BusinessException(CreateOrderContents.PRO_MARKETABLE_MSG);
            } else if (productDto.getStock().intValue() <= 0 || productDto.getStock().intValue() < productParamsDTO.getQuantity() || (productDto.getStock().intValue() - productDto.getAllocatedStock()) < productParamsDTO.getQuantity()) {
                //判斷购买商品库存
                throw new BusinessException(CreateOrderContents.PRO_STOCK_INSUFFICIENT_MSG);
            }
            if (productParamsDTO.getActiveId() != null && productParamsDTO.getActiveId() > 0) {
                //ActiveDTO activeDTO = activeService.selectActiveById(productParamsDTO.getActiveId());
                ////判断活动是否存在
                //if (activeDTO == null) {
                //    throw new BusinessException(OrderContents.ACTIVE_NO_EXIT_MSG);
                //} else if (activeDTO.getIsVaild() == 0 || new Date().getTime() > activeDTO.getEndDate().getTime()) {
                //    //判断活动是否有效
                //    throw new BusinessException(OrderContents.ACTIVE_FAILURE_MSG);                }
                ////判断商品是否属于当前活动
                //ActiveProductRelationDTO activeProductRelationDTO = jxProductService.getActiveProductRelationByActiveAndProductId(productParamsDTO.getActiveId(), productParamsDTO.getProductId());
                //if (StringUtils.isEmpty(activeProductRelationDTO)) {
                //    throw new BusinessException(OrderContents.ACTIVE_PRO_NO_EXIT_MSG);
                //} else if (activeProductRelationDTO.getIsVaild() == 0) {
                //    throw new BusinessException(OrderContents.ACTIVE_PRO_FAILURE_MSG);
                //}
            }
        }
        //判断关键参数是否合法
        if (StringUtils.isEmpty(createOrderParams.getOpenId())) {
            throw new BusinessException(CreateOrderContents.OPENID_ERROR_MSG);
        } else if (CollectionUtils.isEmpty(createOrderParams.getProductParamsDTOs())) {
            throw new BusinessException(CreateOrderContents.PRO_ERROR_MSG);
        } else if (StringUtils.isEmpty(createOrderParams.getConsignee()) || StringUtils.isEmpty(createOrderParams.getPhone()) || StringUtils.isEmpty(createOrderParams.getAddress()) || StringUtils.isEmpty(createOrderParams.getArea())) {
            throw new BusinessException(CreateOrderContents.ADDRESS_ERROR_MSG);
        } else if (createOrderParams.getTotalPrice().compareTo(BigDecimal.ZERO) <= 0) {
            throw new BusinessException(CreateOrderContents.ORDER_AMOUNT_ERROR_MSG);
        }

        //判断订单金额是否大于0
        if (BigDecimal.ZERO.compareTo(createOrderParams.getTotalPrice()) >= 0) {
            throw new BusinessException(CreateOrderContents.ORDER_AMOUNT_ERROR_MSG);
        }
        //重新计算订单金额，判断两次订单金额是否一致
        SumOrderResultDTO sumOrderResultDTO = sumOrderAmount(createOrderParams.getProductParamsDTOs());
        Double totalPrice1Val = sumOrderResultDTO.getTotalPrice();
        if (sumOrderResultDTO == null || createOrderParams.getTotalPrice().compareTo(BigDecimal.valueOf(totalPrice1Val)) != 0) {
            throw new BusinessException(CreateOrderContents.AMOUNT_ERROR_MSG);
        }
        //判断该用户24小时内下单数量,每天最多下30单
        int orderCount = repository.getOrderCountByMemberIdAndHalfHour(InvocationInfoProxy.getUserId());
        if (orderCount > 30) {
            throw new BusinessException(CreateOrderContents.OUT_ORDER_ERROR_MSG);
        }
    }

    /**
     * 验签
     *
     * @param openId
     * @param memberId
     * @param timeStamp
     * @param sign
     * @return
     */
    public Boolean checkSign(String openId, String memberId, String timeStamp, String sign) {
        log.info("签名验证 openId: {},memberId: {},timeStamp: {},sign: {}", openId, memberId, timeStamp, sign);
        String redisTimeStamp = (String) redisTemplate.opsForValue().get(memberId);
        Map<String, String> params = new HashMap<String, String>(3);
        params.put("openId", openId);
        params.put("memberId", memberId);
        params.put("timeStamp", redisTimeStamp);
        Boolean flag = SignUtils.checkSign(params, sign, secretConfig.getSignSecretKey());
        return flag;
    }


    /**
     * 计算订单金额
     *
     * @param paramsDTOList
     * @return
     */
    public SumOrderResultDTO sumOrderAmount(List<ProductParamsDTO> paramsDTOList) {
        Double proPrice = 0.00;
        Double reaFreight = 0.00;
        Double totalPrice = 0.00;

        //一次查出商品，避免多次调用商品服务
        List<Long> productIds = paramsDTOList.stream().map(ProductParamsDTO::getProductId).collect(Collectors.toList());
        Long[] ids = (Long[]) productIds.toArray();
        List<ProductDto> productDtos = productApi.findByIdIn(ids);

        for (ProductParamsDTO productParamsDTO : paramsDTOList) {
            log.info("productParamsDTO productId: {}, quantity{}", productParamsDTO.getProductId(), productParamsDTO.getQuantity());
            //取商品信息
            List<ProductDto> productDtoList = productDtos.stream().filter(pro -> {
                return Objects.equals(pro.getId(), productParamsDTO.getProductId());
            }).collect(Collectors.toList());
            ProductDto productDto = productDtoList.get(0);

            proPrice += productParamsDTO.getQuantity() * productDto.getPrice().doubleValue();
            reaFreight = reaFreight + (productDto.getFreight().doubleValue() * productParamsDTO.getQuantity());
            totalPrice += productParamsDTO.getQuantity() * productDto.getPrice().doubleValue();
        }

        DecimalFormat df = new DecimalFormat("#.00");
        String totalPriceS = df.format(totalPrice + reaFreight);

        log.info("计算结果 totalPrice: {}, proPrice: {}, reaFreight: {}", totalPrice + reaFreight, proPrice, reaFreight);
        SumOrderResultDTO sumOrderResultDTO = new SumOrderResultDTO(Double.valueOf(totalPriceS), proPrice, reaFreight);
        return sumOrderResultDTO;
    }

}
